/*
 * Copyright (c) 2007 Aleksey Nikiforov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'Aleksey Nikiforov' nor the names of other contributors 
 *   may be used to endorse or promote products derived from this software 
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.lex.input.mouse.component;


import java.util.logging.Logger;

import org.lex.input.mouse.Cursor;

import com.jme.input.InputHandler;
import com.jme.input.MouseInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.Vector2f;

/**
 * enable() and disable() methods are not guaranteed to always succeed.
 * You can verify by calling isEnabled().
 * 
 * @author lex
 * 
  */
public class HardwareMouse implements BasicMouse {

	@SuppressWarnings("unused")
	private static final Logger log = Logger.getLogger(
			HardwareMouse.class.getName());

	public static HardwareMouse get() { return mouse; }
	private static final HardwareMouse mouse = new HardwareMouse();
	
	
	private Vector2f absolute = new Vector2f();
	private Cursor cursor;
	
	private MouseInput mouseInput = MouseInput.get();
	
	private InputHandler currentHandler;
		
	/**
	 * It is nessesary to keep absolute coordinates separately, because
	 * if we disable hardware mouse and then move it while suspended
	 * MouseInput.get().getXAbsolute() and MouseInput.get().getXAbsolute()
	 * will change. And the functionality we want is for getHotSpotOffset()
	 * to return unchanged coordinates if mouse is moved while disabled. 
	 */
	private InputAction updateAction = new InputAction() {
		public void performAction(InputActionEvent evt) {
			if (isEnabled()) {
				absolute.x = mouseInput.getXAbsolute();
				absolute.y = mouseInput.getYAbsolute();
			}
		}
	};
	
	/**
	 * This constructor will create the HardwareMouse. The constructor will
	 * not alter the mouse state: if the native mouse was enabled
	 * (for example via MouseInput.get().setCursorVisible(true)),
	 * the HarwareMouse will be enabled, if the native mouse was not enabled so
	 * will be the HarwareMouse.
	 */
	private HardwareMouse() {
		this.cursor = defaultCursor;
	}
	
	public void registerWithInputHandler(InputHandler handler) {
		if (currentHandler != null) {
            currentHandler.removeAction(updateAction);
        }
		
        if (handler != null) {
            handler.addAction(updateAction,
            	InputHandler.DEVICE_MOUSE, InputHandler.BUTTON_NONE, 0, true);
        }
        
        currentHandler = handler;
	}
	
	public void enable() {
		if (!isEnabled()) {
			setCursorVisible(true);
			// this should be called after the cursor is set visible
			// or the position will not be set in some cases
			mouseInput.setCursorPosition((int) absolute.x, (int) absolute.y);
		}
	}
	
	public void disable() {
		if (isEnabled()) {
			setCursorVisible(false);
		}
	}
	
	public boolean isEnabled() {
		return mouseInput.isCursorVisible();
	}

	public Vector2f getHotSpotPosition() {
		return absolute;
	}

	public void setHotSpotPosition(float x, float y) {
		absolute.set(x, y);
		if (isEnabled()) {
			mouseInput.setCursorPosition((int) absolute.x, (int) absolute.y);
		}
	}
	
	public void setHotSpotPosition(Vector2f position) {
		absolute.set(position);
		if (isEnabled()) {
			mouseInput.setCursorPosition((int) absolute.x, (int) absolute.y);
		}
	}
	
	/**
	 * Setting null cursor will cause the default cursor to be used
	 * (defined in BaseMouse). The default system cursor is not used
	 * because using it causes errors when enabling/disabling mouse quickly.
	 * @see org.lex.input.mouse.component.BasicMouse#setCursor(org.lex.input.mouse.Cursor)
	 */
	public void setCursor(Cursor cursor) {
		if (cursor == null) this.cursor = defaultCursor;
		else this.cursor = cursor;
		
		if (isEnabled()) setCursorVisible(true);
	}
	
	public Cursor getCursor() {
		return cursor;
	}
	
	private void setCursorVisible(boolean visible) {
		if (visible) {
			
			mouseInput.setHardwareCursor(cursor.getUrl(),
					cursor.getImages(),
					cursor.getDelays(),
					(int) cursor.getHotSpotOffset().x,
					(int) cursor.getHotSpotOffset().y);
			
		} else {
			mouseInput.setCursorVisible(false);
		}
	}

}
